home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / circuits / pcb-1.000 / pcb-1 / pcb-1.3 / fileselect.c < prev    next >
C/C++ Source or Header  |  1995-02-27  |  18KB  |  600 lines

  1. /*
  2.  *                            COPYRIGHT
  3.  *
  4.  *  PCB, interactive printed circuit board design
  5.  *  Copyright (C) 1994,1995 Thomas Nau
  6.  *
  7.  *  This program is free software; you can redistribute it and/or modify
  8.  *  it under the terms of the GNU General Public License as published by
  9.  *  the Free Software Foundation; either version 2 of the License, or
  10.  *  (at your option) any later version.
  11.  *
  12.  *  This program is distributed in the hope that it will be useful,
  13.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.  *  GNU General Public License for more details.
  16.  *
  17.  *  You should have received a copy of the GNU General Public License
  18.  *  along with this program; if not, write to the Free Software
  19.  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  *
  21.  *  Contact addresses for paper mail and Email:
  22.  *  Thomas Nau, Schlehenweg 15, 88471 Baustetten, Germany
  23.  *  Thomas.Nau@rz.uni-ulm.de
  24.  *
  25.  */
  26.  
  27. static    char    *rcsid = "$Header: /sda4/users/nau/src/pcb/RCS/fileselect.c,v 2.1 1994/09/28 14:26:10 nau Exp nau $";
  28.  
  29. /* file select box
  30.  * some of the actions are local to this module
  31.  */
  32.  
  33. #include <stdio.h>
  34. #include <stdlib.h>
  35. #include <string.h>
  36. #include <dirent.h>
  37. #include <memory.h>
  38. #include <sys/types.h>
  39. #include <sys/stat.h>
  40. #include <unistd.h>
  41.  
  42. #include "global.h"
  43.  
  44. #include "data.h"
  45. #include "dialog.h"
  46. #include "error.h"
  47. #include "fileselect.h"
  48. #include "mymem.h"
  49. #include "misc.h"
  50.  
  51. #include <X11/Shell.h>
  52. #include <X11/Xaw/AsciiText.h>
  53. #include <X11/Xaw/Command.h>
  54. #include <X11/Xaw/Form.h>
  55. #include <X11/Xaw/Label.h>
  56. #include <X11/Xaw/List.h>
  57. #include <X11/Xaw/MenuButton.h>
  58. #include <X11/Xaw/SimpleMenu.h>
  59. #include <X11/Xaw/SmeBSB.h>
  60. #include <X11/Xaw/Scrollbar.h>
  61. #include <X11/Xaw/Viewport.h>
  62.  
  63. /* ---------------------------------------------------------------------------
  64.  * some local types
  65.  */
  66. typedef struct                        /* a select box */
  67. {
  68.     String            Name;            /* the name of the list widget */
  69.     Widget            ViewportW,        /* the listwidget and its manager */
  70.                     ListW;
  71.     String            *Entries;        /* list of entries */
  72.     int                Number,            /* number of entries */
  73.                     MaxNumber;        /* maximum number */
  74.     XtCallbackProc    Callback;        /* notify callback */
  75.     XtPointer        ClientData;
  76. } SelectorType, *SelectorTypePtr;
  77.  
  78. /* ---------------------------------------------------------------------------
  79.  * some local prototypes
  80.  */
  81. static    int        CompareFunction(const void *, const void *);
  82. static    void    AddEntryToList(char *, SelectorTypePtr);
  83. static    void    FreeListEntries(SelectorTypePtr);
  84. static    int        ChangeDirectory(char *);
  85. static    void    SetInputWidget(String);
  86. static    void    CB_CancelOrOK(Widget, XtPointer, XtPointer);
  87. static    void    CB_Menu(Widget, XtPointer, XtPointer);
  88. static    void    CB_Directory(Widget, XtPointer, XtPointer);
  89. static    void    CB_File(Widget, XtPointer, XtPointer);
  90. static    void    CB_Text(Widget, XtPointer, XtPointer);
  91. static    int        FillLists(char *);
  92. static    int        FillListsFromCommand(char *);
  93. static    int        FillListsFromDirectory(char *);
  94. static    Widget    CreateMenuFromPath(Widget, Widget, Widget, char *);
  95. static    Widget    CreateSelector(Widget, Widget, Widget, SelectorTypePtr);
  96.  
  97. /* ---------------------------------------------------------------------------
  98.  * some local identifiers
  99.  */
  100. static    Widget                InputW,                /* input field */
  101.                             CurrentW;            /* label (current directory) */
  102. static    int                    ReturnCode;            /* returncode of buttons */
  103. static    String                CurrentDir;            /* current directory */
  104. static    Boolean                LockCallback;        /* used by CB_Text() */
  105. static    SelectorType        DirectorySelector =
  106.     { "directoryList", NULL,NULL,NULL, 0, 0, CB_Directory, &DirectorySelector };
  107. static    SelectorType        FileSelector =
  108.     { "fileList", NULL, NULL, NULL, 0, 0, CB_File, &FileSelector };
  109. static    DialogButtonType    Buttons[] = {
  110.     {"defaultButton","   OK   ",CB_CancelOrOK,(XtPointer) OK_BUTTON,NULL},
  111.     {"cancelButton","No/Cancel",CB_CancelOrOK,(XtPointer) CANCEL_BUTTON,NULL}};
  112.  
  113. /* ----------------------------------------------------------------------
  114.  * compare function for qsort() routine
  115.  * used to sort list by name
  116.  */
  117. static int CompareFunction(const void *P1, const void *P2)
  118. {
  119.     return(strcmp(*((char **) P1), *((char **) P2)));
  120. }
  121.  
  122. /* ---------------------------------------------------------------------------
  123.  * adds a new filename to the list;
  124.  * memory is allocated as needed;
  125.  * memory for the entry itself is already allocated
  126.  */
  127. static void AddEntryToList(char *Filename, SelectorTypePtr Selector)
  128. {
  129.         /* allocate more memory */
  130.     if (Selector->Number >= Selector->MaxNumber)
  131.     {
  132.         Selector->MaxNumber += STEP_FILENAME;
  133.         Selector->Entries = MyRealloc(Selector->Entries,
  134.             Selector->MaxNumber *sizeof(char *), "AddEntryToList()");
  135.         memset(Selector->Entries +Selector->Number, 0,
  136.             STEP_FILENAME*sizeof(char *));
  137.     }
  138.     Selector->Entries[Selector->Number++] = Filename;
  139. }
  140.  
  141. /* ---------------------------------------------------------------------------
  142.  * releases all memory that is allocated by list entries, not the list
  143.  * itself
  144.  */
  145. static void FreeListEntries(SelectorTypePtr Selector)
  146. {
  147.     while (Selector->Number)
  148.         MyFree(&Selector->Entries[--Selector->Number]);
  149. }
  150.  
  151. /* ----------------------------------------------------------------------
  152.  * changes current directory and prints an error message if failed
  153.  */
  154. static int ChangeDirectory(char *DirName)
  155. {
  156.     if (chdir(DirName))
  157.     {
  158.         ChdirErrorMessage(DirName);
  159.         return(1);
  160.     }
  161.     return(0);
  162. }
  163.  
  164. /* ---------------------------------------------------------------------------
  165.  * set contents of input widget to string
  166.  * replace old string
  167.  * this way resizes the widget if necessary
  168.  */
  169. static void SetInputWidget(String S)
  170. {
  171.     Widget                source = XawTextGetSource(InputW);
  172.     XawTextPosition        last;
  173.     XawTextBlock        block;
  174.  
  175.         /* get last character position */
  176.     last = XawTextSourceScan(source, 0, XawstAll, XawsdRight, 1, True);
  177.     block.ptr = S;
  178.     block.firstPos = 0;
  179.     block.length = strlen(S);
  180.     block.format = FMT8BIT;
  181.  
  182.         /* set 'lock' flag for next call of CB_Text() */
  183.     LockCallback = True;
  184.  
  185.         /* replace contents by new string */
  186.     XawTextReplace(InputW, 0, last, &block);
  187.     XawTextSetInsertionPoint(InputW, block.length);
  188. }
  189.  
  190. /* ---------------------------------------------------------------------------
  191.  * callback function for OK and cancel button
  192.  * also called via accelerators from a double click
  193.  * ClientData identifies the button
  194.  */
  195. static void CB_CancelOrOK(Widget W, XtPointer ClientData, XtPointer CallData)
  196. {
  197.     ReturnCode = (int) ClientData;
  198. }
  199.  
  200. /* ---------------------------------------------------------------------------
  201.  * callback function for rolldown menus
  202.  * directory name is passed as ClientData
  203.  */
  204. static void CB_Menu(Widget W, XtPointer ClientData, XtPointer CallData)
  205. {
  206.     FillLists((char *) ClientData);
  207. }
  208.  
  209. /* ---------------------------------------------------------------------------
  210.  * callback function for directory list
  211.  * gets current selection and updates the list widgets
  212.  */
  213. static void CB_Directory(Widget W, XtPointer ClientData, XtPointer CallData)
  214. {
  215.     char                newdir[MAXPATHLEN+1];
  216.     XawListReturnStruct    *selected = XawListShowCurrent(W);
  217.  
  218.         /* abort if nothing has been selected or if the selected thing is
  219.          * an external command
  220.          */
  221.     if (selected->list_index == XAW_LIST_NONE ||
  222.         *selected->string == '|')
  223.         return;
  224.  
  225.         /* create complete new path; update list */
  226.     sprintf(newdir, "%s/%s", CurrentDir, selected->string);
  227.     FillLists(newdir);
  228. }
  229.  
  230. /* ---------------------------------------------------------------------------
  231.  * callback function for file list
  232.  * gets current selection and updates 'input widget'
  233.  */
  234. static void CB_File(Widget W, XtPointer ClientData, XtPointer CallData)
  235. {
  236.     XawListReturnStruct    *selected = XawListShowCurrent(W);
  237.  
  238.     if (selected->list_index != XAW_LIST_NONE)
  239.         SetInputWidget(selected->string);
  240. }
  241.  
  242. /* ---------------------------------------------------------------------------
  243.  * callback function for input text widget
  244.  * invalidates file selections whenever the buffer changes
  245.  * we have to prevent from unhighlighting the selection that has
  246.  * just updated the buffer by evaluating the 'LockCallback' flag
  247.  */
  248. static void CB_Text(Widget W, XtPointer ClientData, XtPointer CallData)
  249. {
  250.     if (!LockCallback)
  251.         XawListUnhighlight(FileSelector.ListW);
  252.     else
  253.         LockCallback = False;
  254. }
  255.  
  256. /* ---------------------------------------------------------------------------
  257.  * reads contents of the passed directory into the list
  258.  * or calls an external command to create the list
  259.  */
  260. static int FillLists(char *DirName)
  261. {
  262.     if ((*DirName == '|' && !FillListsFromCommand(DirName)) ||
  263.         !FillListsFromDirectory(DirName))
  264.     {
  265.             /* sort lists by name */
  266.         qsort(FileSelector.Entries, FileSelector.Number,
  267.             sizeof(char *), CompareFunction);
  268.         qsort(DirectorySelector.Entries, DirectorySelector.Number,
  269.             sizeof(char *), CompareFunction);
  270.  
  271.             /* update list widgets and current selection */
  272.         XawListChange(FileSelector.ListW, FileSelector.Entries,
  273.             FileSelector.Number, 0, True);
  274.         XawListChange(DirectorySelector.ListW, DirectorySelector.Entries,
  275.             DirectorySelector.Number, 0, True);
  276.  
  277.             /* clear input field; update label */
  278.         XtVaSetValues(InputW, XtNstring, "", NULL);
  279.         XtVaSetValues(CurrentW, XtNlabel, CurrentDir, NULL);
  280.         return(0);
  281.     }
  282.     return(1);
  283. }
  284.  
  285. /* ----------------------------------------------------------------------
  286.  * calls an external command and read its stdout into the list widget;
  287.  * external commands starts with '|'
  288.  */
  289. static int FillListsFromCommand(char *Command)
  290. {
  291.     FILE    *fp;
  292.     char    line[256];
  293.     int        i;
  294.  
  295.         /* ignore leading '|', execute command */
  296.     if ((fp = popen(Command+1, "r")) == NULL)
  297.     {
  298.         PopenErrorMessage(Command+1);
  299.         return(1);
  300.     }
  301.  
  302.     FreeListEntries(&FileSelector);
  303.     FreeListEntries(&DirectorySelector);
  304.     while (fgets(line, 256, fp))
  305.     {
  306.             /* ignore newline and carriage return */
  307.         for (i = strlen(line)-1; i >= 0 ; i--)
  308.             if (line[i] == '\n' || line[i] == '\r')
  309.                 line[i] = '\0';
  310.             else
  311.                 break;
  312.         AddEntryToList(MyStrdup(line, "FillListsFromCommand()"), &FileSelector);
  313.     }
  314.  
  315.     if(pclose(fp))
  316.     {
  317.         FreeListEntries(&FileSelector);
  318.         return(1);
  319.     }
  320.  
  321.         /* update label */
  322.     CurrentDir = Command;
  323.     XtVaSetValues(CurrentW, XtNlabel, Command, NULL);
  324.     return(0);
  325. }
  326.  
  327. /* ---------------------------------------------------------------------------
  328.  * reads contents of the passed directory into the list
  329.  */
  330. static int FillListsFromDirectory(char *DirName)
  331. {
  332.     DIR                *dir;
  333.     struct dirent    *direntry;
  334.     struct stat        buffer;
  335.     char            *entry;
  336.  
  337.         /* open directory */
  338.     if (ChangeDirectory(DirName))
  339.         return(1);
  340.     CurrentDir = GetWorkingDirectory();
  341.     if ((dir = opendir(CurrentDir)) == NULL)
  342.     {
  343.         OpendirErrorMessage(CurrentDir);
  344.         return(1);
  345.     }
  346.  
  347.         /* release memory */
  348.     FreeListEntries(&DirectorySelector);
  349.     FreeListEntries(&FileSelector);
  350.  
  351.         /* read all entries */
  352.     while ((direntry = readdir(dir)) != NULL)
  353.     {
  354.             /* check if entry is directory ...
  355.              * ignore entry on error
  356.              */
  357.         if (!stat(direntry->d_name, &buffer))
  358.         {
  359.                 /* add entry to crresponding list */
  360.             if (S_ISDIR(buffer.st_mode))
  361.             {
  362.                     /* ignore '.' entry */
  363.                 if (!strcmp(direntry->d_name, "."))
  364.                     continue;
  365.  
  366.                     /* allocate memory for string plus mark */
  367.                 entry = MyCalloc(strlen(direntry->d_name)+2,
  368.                     sizeof(char), "FillListsFromDirectory()");
  369.                 sprintf(entry, "%s/", direntry->d_name);
  370.                 AddEntryToList(entry, &DirectorySelector);
  371.             }
  372.             else
  373.             {
  374.                 entry = MyStrdup(direntry->d_name, "FillListsFromDirectory()");
  375.                 AddEntryToList(entry, &FileSelector);
  376.             }
  377.         }
  378.     }
  379.     closedir(dir);
  380.  
  381.         /* add at least '..' just in case the directory isn't readable */
  382.     if (!DirectorySelector.Number)
  383.         AddEntryToList(MyStrdup("../", "FillListsFromDirectory()"),
  384.             &DirectorySelector);
  385.  
  386.     return(0);
  387. }
  388.  
  389. /* ----------------------------------------------------------------------
  390.  * creates menu-button with entries from PATH information
  391.  ' 'copy' needs to be static because a pointer to that memory location
  392.  * is returned
  393.  */
  394. static Widget CreateMenuFromPath(Widget Parent,
  395.     Widget Top, Widget Left,
  396.     char * Path)
  397. {
  398.             Widget    popup,
  399.                     menubutton,
  400.                     entry;
  401.             char    *path;
  402.             Boolean    first;
  403.     static    char    *copy;
  404.  
  405.         /* create button */
  406.     menubutton = XtVaCreateManagedWidget("preset", menuButtonWidgetClass,
  407.         Parent,
  408.         LAYOUT_TOP,
  409.         XtNfromVert, Top,
  410.         XtNfromHoriz, Left,
  411.         XtNlabel, "click here for preset directories",
  412.         XtNmenuName, "preset",
  413.         NULL);
  414.  
  415.         /* create a copy of the path; used for passing to callback */
  416.     MyFree(©);
  417.     copy = MyStrdup(Path, "CreateMenuFromPath()");
  418.  
  419.         /* append menu with paths */
  420.     popup = XtVaCreatePopupShell("preset", simpleMenuWidgetClass,
  421.         menubutton,
  422.         XtNlabel, NULL,
  423.         XtNsensitive, True,
  424.         NULL);
  425.     for (first = True, path = strtok(copy, ":"); path; path = strtok(NULL, ":"))
  426.     {
  427.             /* copy first path element to CurrentDir */
  428.         if (first)
  429.         {
  430.             CurrentDir= path;
  431.             first = False;
  432.         }
  433.         entry = XtVaCreateManagedWidget("directory", smeBSBObjectClass,
  434.             popup,
  435.             XtNlabel, path,
  436.             XtNsensitive, True,
  437.             NULL);
  438.         XtAddCallback(entry, XtNcallback, CB_Menu, (XtPointer) path);
  439.     }
  440.     return(menubutton);
  441. }
  442.  
  443. /* ---------------------------------------------------------------------------
  444.  * creates a selector (list widget managed by a viewport)
  445.  */
  446. static Widget CreateSelector(Widget Parent,
  447.     Widget Top, Widget Left,
  448.     SelectorTypePtr Selector)
  449. {
  450.     Selector->ViewportW=XtVaCreateManagedWidget("viewport", viewportWidgetClass,
  451.         Parent,
  452.         XtNfromVert, Top,
  453.         XtNfromHoriz, Left,
  454.         LAYOUT_NORMAL,
  455.         XtNallowHoriz, True,
  456.         XtNallowVert, True,
  457.         XtNuseBottom, True,
  458.         NULL);
  459.  
  460.     Selector->ListW = XtVaCreateManagedWidget(Selector->Name, listWidgetClass,
  461.         Selector->ViewportW,
  462.         XtNdefaultColumns, 1,
  463.         XtNforceColumns, True,
  464.         NULL);
  465.     if (Selector->Callback)
  466.         XtAddCallback(Selector->ListW, XtNcallback,
  467.             Selector->Callback, Selector->ClientData);
  468.  
  469.     return(Selector->ViewportW);
  470. }
  471.  
  472. /* ---------------------------------------------------------------------------
  473.  * file select box
  474.  * a static pointer to the selected file is returned -->
  475.  * it's only valid till it's called again
  476.  * the directories in the passed path are available from a menu
  477.  */
  478. char *FileSelectBox(char *MessageText, char *DefaultFile, char *Path)
  479. {
  480.             Widget    popup,
  481.                     masterform,
  482.                     last;
  483.             char    currentdir[MAXPATHLEN+1],
  484.                     *file;
  485.     static    char    result[MAXPATHLEN+1];
  486.  
  487.         /* save current directory */
  488.     strcpy(currentdir, GetWorkingDirectory());
  489.  
  490.         /* create the popup shell */
  491.     popup = XtVaCreatePopupShell("popup", transientShellWidgetClass,
  492.         Output.Toplevel,
  493.         XtNtransientFor, Output.Toplevel,
  494.         XtNallowShellResize, True,
  495.         XtNmappedWhenManaged, False,
  496.         XtNinput, True,
  497.         NULL);
  498.  
  499.         /* the form that holds everything */
  500.     masterform = XtVaCreateManagedWidget("selectMasterForm", formWidgetClass,
  501.         popup,
  502.         XtNresizable, True,
  503.         NULL);
  504.  
  505.         /* the line which displays the some text */
  506.     last = XtVaCreateManagedWidget("comment", labelWidgetClass,
  507.         masterform,
  508.         LAYOUT_TOP,
  509.         XtNlabel, MessageText,
  510.         NULL);
  511.  
  512.         /* create a menu button for preset directories, also
  513.          * initializes 'CurrentDir'
  514.          */
  515.     last = CreateMenuFromPath(masterform, last, NULL,
  516.         (Path && *Path) ? Path : ".");
  517.  
  518.         /* the line which displays the current directory */
  519.     CurrentW = XtVaCreateManagedWidget("current", labelWidgetClass,
  520.         masterform,
  521.         XtNresizable, True,
  522.         XtNfromVert, last,
  523.         LAYOUT_TOP,
  524.         NULL);
  525.  
  526.     InputW = XtVaCreateManagedWidget("input", asciiTextWidgetClass,
  527.         masterform,
  528.         XtNresizable, True,
  529.         XtNresize, XawtextResizeWidth,
  530.         XtNwrap, XawtextWrapNever,
  531.         XtNeditType, XawtextEdit,
  532.         XtNfromVert, CurrentW,
  533.         LAYOUT_TOP,
  534.         NULL);
  535.     XtAddCallback(XawTextGetSource(InputW), XtNcallback, CB_Text, NULL);
  536.  
  537.         /* the two selectors and the buttons */
  538.     last = CreateSelector(masterform, InputW, NULL, &DirectorySelector);
  539.     last = CreateSelector(masterform, InputW, last, &FileSelector);
  540.     AddButtons(masterform, last, Buttons, ENTRIES(Buttons));
  541.     
  542.         /* override the translations for the input widget and
  543.          * install accelerators for the buttons
  544.          * !!! first translations, second accelerators !!!
  545.          * additionaly ignore 'spaces' and 'slashes' during input
  546.          */
  547.     XtOverrideTranslations(InputW, XtParseTranslationTable(InputTranslations));
  548.     XtOverrideTranslations(InputW,
  549.         XtParseTranslationTable("<Key>space: no-op()\n <Key>slash: no-op()\n"));
  550.     XtInstallAccelerators(InputW, Buttons[0].W);
  551.     XtInstallAccelerators(InputW, Buttons[1].W);
  552.  
  553.         /* set keyboard focus to input widget */
  554.     XtSetKeyboardFocus(masterform, InputW);
  555.  
  556.         /* install additional accelerators for the default button;
  557.          * double click to select
  558.          * install translation so that selection still works
  559.          */
  560.     XtOverrideTranslations(FileSelector.ListW,
  561.         XtParseTranslationTable("<Btn1Down>: Set() Notify()\n"));
  562.     XtInstallAccelerators(FileSelector.ListW, Buttons[0].W);
  563.  
  564.         /* read directory structure of current directory
  565.          * which was set to the first menu entry from CreateMenuFromPath()
  566.          * set text widget to default and reset lock-flag
  567.          */
  568.     FillLists(CurrentDir);
  569.     SetInputWidget(DefaultFile ? DefaultFile : "");
  570.     LockCallback = False;
  571.  
  572.         /* now display dialog and wait for completion */
  573.     StartDialog(popup);
  574.     DialogEventLoop(&ReturnCode);
  575.  
  576.         /* evaluate selection */
  577.     *result = '\0';
  578.     if (ReturnCode == OK_BUTTON)
  579.     {
  580.         XtVaGetValues(InputW, XtNstring, &file, NULL);
  581.  
  582.             /* append result to current directory */
  583.         if (file && *file)
  584.             if (*CurrentDir != '|')
  585.                 sprintf(result, "%s/%s", CurrentDir, file);
  586.             else
  587.                 strcpy(result, file);
  588.     }
  589.  
  590.         /* remove event handler, dialog, release resources and
  591.          * change back to initial directory
  592.          */
  593.     EndDialog(popup);
  594.     FreeListEntries(&DirectorySelector);
  595.     FreeListEntries(&FileSelector);
  596.     ChangeDirectory(currentdir);
  597.  
  598.     return(*result ? result : NULL);
  599. }
  600.